package net.kazed.nextaction.database;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.content.ContentResolver;
import android.database.Cursor;

/**
 * Cache for GtdContext objects.
 * 
 * @author Koert Zeilstra
 */
public class ContextCache {

   private ContentResolver contentResolver;
   private List<GtdContext> flattenedContextList;
   private TreeNode<GtdContext> rootNode;


   /**
    * Constructor.
    * 
    * @param contentResolver Data accessor.
    */
   public ContextCache(ContentResolver contentResolver) {
      super();
      this.contentResolver = contentResolver;
   }

   /**
    * Clear cached data.
    */
   public void clearCache() {
      flattenedContextList = null;
      rootNode = null;
   }
   
   /**
    * @return Root node of context tree.
    */
   public TreeNode<GtdContext> getContextTree() {
      if (rootNode == null) {
         rootNode = new TreeNode<GtdContext>(null, null);
         Cursor cursor = null;
         try {
            cursor = contentResolver.query(GtdContext.CONTENT_URI, GtdContext.FULL_PROJECTION.getFieldNames(),
                  GtdContext.PARENT_CONTEXT_ID + " is null", null, GtdContext.NAME + " ASC");
            addChildNodes(cursor, rootNode);
         } finally {
            try {
              cursor.close();
            } catch (Exception e) {
               
            }
         }
      }
      return rootNode;
   }
   
   /**
    * @param contextId ID of context to find.
    * @return Node with GtdContext with contextId, null if not found.
    */
   public TreeNode<GtdContext> getContextTree(Integer contextId) {
      TreeNode<GtdContext> node = findChildNode(contextId, getContextTree());
      return node;
   }

   /**
    * Find child node with contextId.
    * @param contextId ID of context to find.
    * @param parent Parent of sub tree.
    * @return Node with GtdContext with contextId.
    */
   private TreeNode<GtdContext> findChildNode(Integer contextId, TreeNode<GtdContext> parent) {
      TreeNode<GtdContext> foundNode = null;
      for (TreeNode<GtdContext> childNode : parent.getChildren()) {
         if (contextId.equals(childNode.getData().getId())) {
            foundNode = childNode;
         } else {
            TreeNode<GtdContext> node = findChildNode(contextId, childNode);
            if (node != null) {
               foundNode = node;
            }
         }
      }
      return foundNode;
   }
   
   /**
    * Add all GtdContext objects of cursor as children to node.
    * @param cursor Cursor to query GtdContext objects.
    * @param node Add children to this node.
    */
   private void addChildNodes(Cursor cursor, TreeNode<GtdContext> node) {
      for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
         GtdContext context = new GtdContext(cursor);
         TreeNode<GtdContext> childNode = new TreeNode<GtdContext>(node, context);
         node.addChild(childNode);
         Cursor childCursor = null;
         try {
            childCursor = contentResolver.query(GtdContext.CONTENT_URI, GtdContext.FULL_PROJECTION.getFieldNames(),
                  GtdContext.PARENT_CONTEXT_ID + " = ?", new String[] {context.getId().toString()}, GtdContext.NAME + " ASC");
            addChildNodes(childCursor, childNode);
         } finally {
            childCursor.close();
         }
         try {
            childCursor = contentResolver.query(Task.CONTENT_URI, Task.FULL_PROJECTION.getFieldNames(),
                  Task.CONTEXT_ID + " = ?", new String[] {context.getId().toString()}, null);
            int numberOfTasks = childCursor.getCount();
            context.setNumberOfTasks(numberOfTasks);
         } finally {
            childCursor.close();
         }
      }
   }
   
   public List<GtdContext> getFlattenedContextTree() {
      if (flattenedContextList == null) {
         flattenedContextList = new ArrayList<GtdContext>();
         TreeNode<GtdContext> root = getContextTree();
         addFlattenedContextTree(root, flattenedContextList);
      }
      return flattenedContextList;
   }

   private void addFlattenedContextTree(TreeNode<GtdContext> node, List<GtdContext> contextList) {
      for (Iterator<TreeNode<GtdContext>> nodeIterator = node.getChildren().iterator(); nodeIterator.hasNext();) {
         TreeNode<GtdContext> childNode = (TreeNode<GtdContext>) nodeIterator.next();
         contextList.add(childNode.getData());
         addFlattenedContextTree(childNode, contextList);
      }
   }

}
